//测试GGX的分布函数的采样函数的像素着色器

#define PI 3.1415926

//顶点着色器输出
struct VS_OUTPUT
{
	float4 Pos : SV_POSITION;
	float3 Normal : NORMAL;
	float2 TexCoord : TEXCOORD0;
	float3 WorldPosition : TEXCOORD1;
};

TextureCube skyboxCubeMap : register( t9 );
SamplerState ourSampler : register( s5 );

cbuffer SceneData : register( b7 )
{
	matrix View;
	matrix Projection;
	float4 lightDir;
	float4 eyePosition;
	float4 TestValue;
}

float RadicalInverse_VdC(uint bits)
{
	bits = (bits << 16u) | (bits >> 16u);
	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
	return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------
float2 Hammersley(uint i, uint N)
{
	return float2(float(i) / float(N), RadicalInverse_VdC(i));
}

//GGX采样微面元法线
//sampleNum：采样数目，用于产生随机值
//sampleCount：采样总数
//a：GGX中的粗糙度
//N：宏观表面的法线
float3 GGXsampleMicroNormal(int sampleNum, int sampleCount, float a, float3 N)
{
	//使用Hammersley生成随机值：
	float2 rand = Hammersley(sampleNum, sampleCount);
	//公式中输入的两个随机值：
	float xi_1 = rand.x;
	float xi_2 = rand.y;

	//使用公式生成极角：
	float theta = atan(a * sqrt(xi_1) / sqrt(1 - xi_1));
	//使用公式生成方位角：
	float phi = 2 * PI * xi_2;

	//生成一个任意的以法线为Z轴的坐标系（XY轴的朝向是无所谓的，因为phi在2PI内均匀随机）
	float3 NotN = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);//任意一个不是N的方向
	float3 T = normalize(cross(NotN, N));	//叉乘得到切线，一定和法线垂直
	float3 B = cross(N, T);		//叉乘得到副法线，一定和法线以及切线垂直

	//将方向变换到世界空间：
	return T * (sin(theta) * cos(phi)) +
		B * (sin(theta) * sin(phi)) +
		N * cos(theta);
}

float4 Main(VS_OUTPUT input) : SV_Target
{
	//测试的粗糙度
	float TestRoughness =  TestValue.x;

	//视线方向
	float3 V = normalize(eyePosition.xyz - input.WorldPosition);

	//光线方向
	float3 L = normalize(lightDir);

	//半向量
	float3 HalfVector = normalize(L + V);
	
	int sampleCount = 256;	//采样次数
	float result = 0;		//累加的结果，最后将除以sampleCount平均化
	for (int i = 0; i < sampleCount; i++)
	{
		//微面元的法线方向：
		float3 MicroNormal = GGXsampleMicroNormal(i, sampleCount, TestRoughness, normalize(input.Normal));
		
		//作为测试，检查它和光线方向的夹角是否小于20°
		float p = acos(dot(MicroNormal, L)) < PI/9;
		
		//累加
		result += p ;
	}

	//平均采样次数，以获得微平面和光线方向的夹角小于20°的概率
	result /= sampleCount;

	//输出以将概率可视化
	return float4(result, result, result, 1);
}
